﻿using System;
using System.Diagnostics;

using Game.Actions;
using Game.Command;
using Game.Command.Internal;
using Game.Common;
using Game.Controller.Internal;
using Game.Level;
using Game.Map;
using Game.Map.Extensions;
using Game.Types;

namespace Game.Controller
{
    public class GameController 
    {
        readonly GameControllerEvents _Events;
        readonly IGameMapView _GameMap;
        readonly GameLevels _GameLevels;
        readonly MapWrapper _MapWrapper;
        readonly PlayerCollection _PlayerCollection;
        readonly VehicleTypeCollection _VehicleTypeCollection;
        readonly CommandExecutor _CommandExecutor;
        readonly StartTurnVisitor _TurnVisitor;
        readonly PlayerHandler _PlayerHandler;
        readonly AiHandler _AiHandler;
        
        GameState _GameState;
        TurnHandler _TurnHandler;
        
        public GameController(
            IGameMap map,
            GameLevels levels)
        {
            _CommandExecutor = new CommandExecutor();
            _Events = new GameControllerEvents();

            _GameMap = map.Views[HumanPlayer.Player];
            _GameLevels = levels;

            _MapWrapper = new MapWrapper(_GameMap);
            _PlayerCollection = new PlayerCollection();
            _CommandExecutor.After += AfterExecute;
            _VehicleTypeCollection = new VehicleTypeCollection();
            _TurnVisitor = new StartTurnVisitor(this);
            
            _PlayerHandler = new PlayerHandler();
            _AiHandler = new AiHandler();
            
            _GameState = GameState.Stopped;
            
            _GameMap.ClickX().Click += OnClick;
            _MapWrapper.AllBuildingsCaptured += (sender, e) => _GameLevels.Events.FireGameSuccess();
            _MapWrapper.AllHumanVehiclesLost += (sender, e) => _GameLevels.Events.FireGameFailure();
        }

        public GameControllerEvents Events
        {
            get { return _Events; }
        }

        public IGameMapView GameMap
        {
            get { return _GameMap; }
        }

        public GameLevels GameLevels
        {
            get { return _GameLevels; }
        }

        public PlayerCollection PlayerCollection
        {
            get { return _PlayerCollection; }
        }
        
        public VehicleTypeCollection VehicleTypeCollection
        {
            get { return _VehicleTypeCollection; }
        }
        
        public GameState GameState
        {
            get { return _GameState; }
        }
        
        public CommandExecutor CommandExecutor
        {
            get { return _CommandExecutor; }
        }
        
        public IGameMapView GameMapView
        {
            get { return _GameMap; }
        }
        
        public void Start()
        {
            _GameMap.Views[AiPlayer.Player].ResetVisible();
            
            {
                GameTurn turn = GameTurn.Create();
                turn.PlayerWithTurn = HumanPlayer.Player;
                _TurnHandler = _PlayerHandler;
            }
            
            _GameMap.ResetVisible();
            _GameState = GameState.Running;
            _Events.FireChanged();
            _Events.FireGameStart();

            _GameMap.Accept(new ResetVehicleVisitor());
        }
        
        public void Stop()
        {
            _GameState = GameState.Stopped;
            _Events.FireChanged();
            ClearCommands();
        }
        
        public void DoTurn(GameTurn turn)
        {
            _TurnVisitor.Reset();
            
            {
                turn.PlayerWithTurn = HumanPlayer.Player;
                _TurnHandler = _PlayerHandler;
                ResetVehicles();
            }

            {
                turn.PlayerWithTurn = AiPlayer.Player;
                _TurnHandler = _AiHandler;
                ResetVehicles();
                ProcessTurn(turn);
            }

            Debug.Assert(turn != null, "turn != null");
            turn.CreateChild();
            
            {
                turn.PlayerWithTurn = HumanPlayer.Player;
                _TurnHandler = _PlayerHandler;
            }

            _CommandExecutor.IncrementTurnsCompleted();
        }

        void ResetVehicles()
        {
            var visitor = new ResetVehicleVisitor();
            _GameMap.Accept(visitor);
        }

        internal void ProcessTurn(GameTurn turn)
        {
            TurnHandler handler;
            if (turn.PlayerWithTurn == AiPlayer.Player)
            {
                handler = _AiHandler;
            }
            else
            {
                handler = _PlayerHandler;
            }
            
            _TurnVisitor.Execute(_GameMap, _GameLevels, handler, turn);
        }

        void AfterExecute(object sender, ActionExecutorEventArgs e)
        {
            if (e.GameTurn.PlayerWithTurn == AiPlayer.Player && e.CanChain)
            {
                ProcessTurn(e.GameTurn);
            }

            if (_CommandExecutor.ExecutingAction is AttackUnitCommand)
            {
                var c = _CommandExecutor.ExecutingAction as AttackUnitCommand;
                _Events.FireFieldAttacked(_GameMap.Fields[c.Path.LastPoint]);
            }
        }
            
        public void ProcessAiTurn()
        {
            GameTurn turn = GameTurn.Create();
            turn.PlayerWithTurn = AiPlayer.Player;
            
            GameAction action = new TurnAction(this);

            _CommandExecutor.ExecuteLater(_GameMap, _GameLevels, turn, action);
        }
        
        void OnClick(object sender, GameMapEventArgs e)
        {
            if (_GameMap.SelectedX().Selected == e.MapField)
            {
                ClearCommands();
                _GameMap.SelectedX().Selected = null;
            }
            else
            {
                var cmd = e.MapField.CommandX().Command;
                ClearCommands();
                if (cmd == null)
                {
                    _GameMap.SelectedX().Selected = e.MapField;
                }

                if (_GameState == GameState.Running)
                {
                    GameTurn turn = GameTurn.Create();
                    turn.PlayerWithTurn = HumanPlayer.Player;
                    _TurnHandler.OnClick(
                        _CommandExecutor, e.MapField,
                        _GameMap, _GameLevels, turn, cmd);
                }
            }
        }

        public void ClearCommands()
        {
            foreach (var field in _GameMap.Fields)
            {
                field.CommandX().Command = null;
                field.ViewX().GameImage = GameImage.None;
            }

            _Events.FireCommandsClear();
        }

    }

    public class MapFieldEventArgs : EventArgs
    {
        readonly IMapField _MapField;

        public MapFieldEventArgs(IMapField field)
        {
            _MapField = field;
        }

        public IMapField MapField
        {
            get { return _MapField; }
        }
    }
}